#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "graph.h"

/*
#define VERBOSE
*/

double graph_factor(Factor_t * p_fac, int * p_var_vals)
{
  int v;
  int index;
  
  index = 0;
  
  for (v=0; v<p_fac->fac_num_vars; v++)
  {
    Variable_t * p_var;
    
    p_var = p_fac->fac_p_vars[v];
    
    index = index * p_var->var_card + p_var_vals[p_var->var_id];
  }

  return p_fac->fac_p_log_entries[index];
}

double graph_joint_factor(Graph_t * p_graph, int * p_var_vals)
{
  int f;
  double log_prod;
  
  log_prod = 0;
  
  for (f=0; f<p_graph->graph_num_facs; f++)
    log_prod += graph_factor(p_graph->graph_p_facs + f, p_var_vals);

  return log_prod;
}

void graph_deduce_neighbors(Graph_t * p_graph)
{
  int num_nbrs;
  Variable_t ** p_nbrs;
  int num_nbr_facs;
  Factor_t ** p_nbr_facs;
  
  int v;
  
  p_nbrs = (Variable_t **) malloc(p_graph->graph_num_vars * sizeof(*p_nbrs));
  p_nbr_facs = (Factor_t **) malloc(p_graph->graph_num_facs 
                                    * sizeof(*p_nbr_facs));
  
  for (v=0; v<p_graph->graph_num_vars; v++)
  {
    Variable_t * p_var;
    int f;
    
    p_var = p_graph->graph_p_vars + v;

    num_nbrs = 0;
    num_nbr_facs = 0;
    
    for (f=0; f<p_var->var_num_facs; f++)
    {
      Factor_t * p_fac;
      int v2;
      
      p_fac = p_var->var_p_facs[f];
      
      for (v2=0; v2<p_fac->fac_num_vars; v2++)
      {
        Variable_t * p_var2;
        int i;
        
        p_var2 = p_fac->fac_p_vars[v2];
        
        /* check if we have already seen this neighbour */
        for (i=0; i<num_nbrs; i++)
        {
          if (p_nbrs[i] == p_var2)
            break;
        }

        if (i == num_nbrs)
        {
          int f2;
          p_nbrs[num_nbrs ++] = p_var2;
          
          for (f2=0; f2<p_var2->var_num_facs; f2++)
          {
            Factor_t * p_fac2;
            int j;
            
            p_fac2 = p_var2->var_p_facs[f2];
            
            /* check if we have seen this factor before */
            for (j=0; j<num_nbr_facs; j++)
            {
              if (p_fac2 == p_nbr_facs[j])
                break;
            }

            if (j == num_nbr_facs)
              p_nbr_facs[num_nbr_facs ++] = p_fac2;
          }
        }
      }
    }

    p_var->var_num_nbr_vars = num_nbrs;
    p_var->var_p_nbr_vars = (Variable_t **)malloc(num_nbrs * sizeof(*p_nbrs));
    memcpy(p_var->var_p_nbr_vars, p_nbrs, num_nbrs * sizeof(*p_nbrs));

    p_var->var_num_nbr_facs = num_nbr_facs;
    p_var->var_p_nbr_facs = (Factor_t **)malloc(num_nbr_facs 
                                                * sizeof(*p_nbr_facs));
    memcpy(p_var->var_p_nbr_facs, p_nbr_facs, num_nbr_facs
           * sizeof(*p_nbr_facs));
  }

#ifdef VERBOSE
  for (v=0; v<p_graph->graph_num_vars; v++)
  {
    Variable_t * p_var;
    int v2;
    int f2;
    
    p_var = p_graph->graph_p_vars + v;
    printf("nbrs vars %d:", v);

    for (v2 = 0; v2<p_var->var_num_nbr_vars; v2++)
    {
      Variable_t * p_var2;
      
      p_var2 = p_var->var_p_nbr_vars[v2];
      
      printf(" %d", p_var2->var_id);
    }

    printf("\n");

    printf("nbrs facs %d:", v);

    for (f2 = 0; f2<p_var->var_num_nbr_facs; f2++)
    {
      Factor_t * p_fac2;
      
      p_fac2 = p_var->var_p_nbr_facs[f2];
      
      printf(" %d", p_fac2->fac_id);
    }
    printf("\n");

  }
#endif /* VERBOSE */
  
  free(p_nbrs);
}

/* returns 1 if an evidence variable was attempted to be modified */
int graph_set_vars(int num_vars, Variable_t ** pp_vars, int index,
                   Instance_t * p_inst)
{
  int v;
  
  for (v=0; v<num_vars; v++)
  {
    Variable_t * p_var;
    int value;
    
    p_var = pp_vars[v];
    
    value = index % p_var->var_card;

    if (p_inst->inst_p_is_obs[p_var->var_id] 
        && (p_inst->inst_p_val[p_var->var_id] != value))
      return 1;

    p_inst->inst_p_val[p_var->var_id] = value;
    
    index /= p_var->var_card;
  }

  return 0;
}
